-
Notifications
You must be signed in to change notification settings - Fork 19
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature/deseng692 and deseng671: Updated video widget, made crucial changes to authoring loaders and actions, added columns to engagement and widget_video tables. #2592
Conversation
…ital fixes to action and loading logic in authoring section, added columns to engagement and widget_video tables.
met-web/src/components/engagement/old-view/widgets/Video/VideoWidgetView.tsx
Fixed
Show fixed
Hide fixed
met-web/src/components/engagement/old-view/widgets/Video/VideoWidgetView.tsx
Fixed
Show fixed
Hide fixed
met-web/src/components/engagement/old-view/widgets/Video/VideoWidgetView.tsx
Fixed
Show fixed
Hide fixed
met-web/src/components/engagement/old-view/widgets/Video/VideoWidgetView.tsx
Fixed
Show fixed
Hide fixed
met-web/src/components/engagement/old-view/widgets/Video/VideoWidgetView.tsx
Fixed
Show fixed
Hide fixed
met-web/src/components/engagement/old-view/widgets/Video/VideoWidgetView.tsx
Fixed
Show fixed
Hide fixed
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #2592 +/- ##
==========================================
- Coverage 75.27% 75.12% -0.15%
==========================================
Files 612 612
Lines 22145 22215 +70
Branches 1839 1859 +20
==========================================
+ Hits 16669 16689 +20
- Misses 5203 5253 +50
Partials 273 273
Flags with carried forward coverage won't be shown. Click here to find out more.
|
<ControlledTextField | ||
name="title" | ||
variant="outlined" | ||
label=" " |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there some other labelling happening here for screen readers?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks Alex, I'll create some labels for screen readers :)
banner_filename: string; | ||
status_block: string[]; | ||
title: string; | ||
icon_name: string; | ||
metadata_value: string; | ||
send_report: boolean; | ||
send_report: boolean | undefined; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
send_report: boolean | undefined; | |
send_report?: boolean; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I need a state for the boolean that is neither true or false, so that if that field isn't set, it isn't submitted in the action. I'm open to suggestions if there's a better way to do it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The question mark means that it's optional; i.e. it can be undefined
banner_filename: data.banner_filename, | ||
status_block: data.status_block, | ||
title: data.title, | ||
icon_name: data.icon_name, | ||
metadata_value: data.metadata_value, | ||
send_report: getSendReportValue(data.send_report), | ||
send_report: data.send_report ? getSendReportValue(data.send_report) : '', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove the getSendReportValue function - this code replicates the same behaviour. If you want it to store the string "false"
instead of ""
when send_report
is turned off, replace the ||
with a ??
nullish coalescing operator.
send_report: data.send_report ? getSendReportValue(data.send_report) : '', | |
send_report: (data.send_report || '').toString() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see the complete solution now. I'll change the logic.
// Update the loader data when the authoring section is changed, by triggering navigation(). | ||
const navigate = useNavigate(); | ||
useEffect(() => { | ||
engagementData.then(() => navigate('.', { replace: true })); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Strongly consider using revalidator.revalidate()
for this
@@ -42,25 +47,48 @@ const AuthoringSummary = () => { | |||
} | |||
}, [fetcher.data]); | |||
|
|||
const { content } = useRouteLoaderData('authoring-loader') as { | |||
content: Promise<EngagementContent[]>; | |||
const untouchedDefaultValues: EngagementUpdateData = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there such a thing as "touched default values"? What does "default" mean here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The defaultValues are the defaultValues for the form. They are passed down from the AuthoringContext and so is setDefaultValues, so that we have full control over the value. It represents the "untouched" state of the form, which will change based on the values that we load from the db. So the regular defaultValues are whatever is loaded into the form. The untouched default values are the blank default values that are never changed.
## September 12, 2024 | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Make sure to also document the new work you did for the Summary section :)
const formatVideoDuration = (duration: number) => { | ||
let minutes = 0; | ||
let hours = 0; | ||
let seconds = 0; | ||
if (3600 < duration) { | ||
hours = Math.floor(duration / 3600); | ||
minutes = Math.floor((duration - hours * 3600) / 60); | ||
seconds = Math.floor(duration - hours * 3600 - minutes * 60); | ||
return `${hours} hr ${minutes} min ${seconds} sec`; | ||
} else if (3600 > duration && 60 < duration) { | ||
minutes = Math.floor(duration / 60); | ||
seconds = Math.floor(duration - minutes * 60); | ||
return `${minutes} min ${seconds} sec`; | ||
} else if (60 > duration && 0 < duration) { | ||
return `${seconds} seconds`; | ||
} else { | ||
return ''; | ||
} | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const formatVideoDuration = (duration: number) => { | |
let minutes = 0; | |
let hours = 0; | |
let seconds = 0; | |
if (3600 < duration) { | |
hours = Math.floor(duration / 3600); | |
minutes = Math.floor((duration - hours * 3600) / 60); | |
seconds = Math.floor(duration - hours * 3600 - minutes * 60); | |
return `${hours} hr ${minutes} min ${seconds} sec`; | |
} else if (3600 > duration && 60 < duration) { | |
minutes = Math.floor(duration / 60); | |
seconds = Math.floor(duration - minutes * 60); | |
return `${minutes} min ${seconds} sec`; | |
} else if (60 > duration && 0 < duration) { | |
return `${seconds} seconds`; | |
} else { | |
return ''; | |
} | |
}; | |
const formatVideoDuration = (duration: number) => { | |
if (duration <= 0) return ''; | |
const hours = Math.floor(duration / 3600); | |
const minutes = Math.floor((duration % 3600) / 60); | |
const seconds = Math.floor(duration % 60); | |
let result = ''; | |
if (hours > 0) result += `${hours} hr `; | |
if (minutes > 0) result += `${minutes} min `; | |
if (seconds > 0 || result === '') result += `${seconds} sec`; | |
return result.trim(); | |
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are a couple of reasons why I didn't implement it this way:
- IMO you would still want all subsequent fields from the biggest unit that has a value, even if the subsequent value is 0
- Although my own code is maybe 4 lines longer and is more basic, I find it more readable
const getVideoSource = (url: string) => { | ||
const hostname = new URL(url).hostname; | ||
if ('youtube.com' === hostname || 'youtu.be' === hostname) { | ||
return 'YouTube'; | ||
} else if ('vimeo.com' === hostname) { | ||
return 'Vimeo'; | ||
} else if ('facebook.com' === hostname) { | ||
return 'Facebook'; | ||
} else if ('twitch.tv' === hostname) { | ||
return 'Twitch'; | ||
} else if ('soundcloud.com' === hostname) { | ||
return 'SoundCloud'; | ||
} else if ('mixcloud.com' === hostname) { | ||
return 'Mixcloud'; | ||
} else if ('dailymotion.com' === hostname) { | ||
return 'DailyMotion'; | ||
} else { | ||
return 'Unknown'; | ||
} | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Replace this large if/else/else... statement with something like an object map.
const domains = {
'youtube.com': { site:"Youtube", icon: faYoutube },
'youtu.be': { site:"Youtube", icon: faYoutube },
'vimeo.com': { site: "Vimeo", icon: faVimeo },
...
}
...
const videoSource = (hostname in domains) ? domains[hostname].site : "Unknown"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I ended up using an array of objects but I did something similar to make it much less verbose.
<Grid item xs={12} sx={{ maxHeight: '0px', padding: '0 !important', margin: '0' }}> | ||
<VideoOverlay | ||
videoOverlayTitle={videoOverlayTitle} | ||
source={getVideoSource(videoWidget.video_url)} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
calculate the video source once and store it as a constant instead of querying it repeatedly inside your JSX. Same goes for the title.
const getIcon = (source: string) => { | ||
if ('YouTube' === source) { | ||
return faYoutube; | ||
} else if ('Vimeo' === source) { | ||
return faVimeo; | ||
} else if ('Facebook' === source) { | ||
return faFacebookF; | ||
} else if ('Twitch' === source) { | ||
return faTwitch; | ||
} else if ('SoundCloud' === source) { | ||
return faSoundcloud; | ||
} else if ('Mixcloud' === source) { | ||
return faMixcloud; | ||
} else if ('DailyMotion' === source) { | ||
return faDailymotion; | ||
} else { | ||
return faQuestionCircle; | ||
} | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again, please replace this large conditional with an object map or other solution. I recommend using the same map to get the source name and icon because they are related 1-1
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Used the same object array from before to solve this issue.
return `${player.getInternalPlayer().element.title} (${source} video)`; | ||
} else if ('Facebook' === source || 'Twitch' === source || 'DailyMotion' === source) { | ||
return `${widgetTitle} (${source} video)`; // API doesn't return video title, use widget title. | ||
} else if ('SoundCloud' === source || 'Mixcloud' === source) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems like sound platforms are adding a lot of complexity to your title parsing. Were these added platforms requested by the PO?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
None of the platforms were added, they were just always enabled. All I'm doing is handling all of the possible inputs. Of course, we can change this by URL checking with YUP on the form if it comes to that.
icon={getIcon(source)} | ||
style={{ fontSize: '1.9rem', paddingRight: '0.65rem', color: '#FCBA19' }} | ||
/>{' '} | ||
<span style={{ fontSize: '0.95rem' }}>{videoOverlayTitle}</span> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please stop creating text with size 0.95rem
or 0.9rem
. This does not correspond to any value used in the designs, and causes rendering issues because it comes out to a fractional pixel value.
To match the designs in Figma, use one of the following:
- <BodyText size="small">
- fontSize: '0.875rem'
- fontSize: '14px'
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Must have missed the exact value, will update. Doesn't really seem to be an issue since browsers have been parsing fractional pixel values for a long time. Please be patient, it's important to be gracious when teaching each other things.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for your work on these tickets Jareth! A few things for you to take a look at 👀
…s, fixed issues with deferred data aborted errors.
@@ -39,7 +39,7 @@ export const getLanguageValue = (currentLanguage: string, languages: Language[]) | |||
const AuthoringTemplate = () => { | |||
const { onSubmit, defaultValues, setDefaultValues, fetcher }: AuthoringContextType = useOutletContext(); | |||
const { engagementId } = useParams() as { engagementId: string }; // We need the engagement ID quickly, so let's grab it from useParams | |||
const { engagement } = useRouteLoaderData('single-engagement') as { engagement: Engagement }; | |||
const { engagement } = useLoaderData() as { engagement: Promise<Engagement> }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const { engagement } = useLoaderData() as { engagement: Promise<Engagement> }; | |
const { engagement } = useLoaderData() as EngagementLoaderData; |
)} | ||
</Await> | ||
</Suspense> | ||
<div style={{ marginTop: '2rem' }}> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for resolving this layout shift issue :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for looking at my suggestions Jareth! I would still recommend refactoring your timestamp calculation to be more readable and I would appreciate you addressing the description_title
controller but I won't block you from merging :)
return 'Unknown'; | ||
} | ||
}; | ||
const videoSources: WidgetVideoSource[] = [ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Really appreciate this cleaned up approach :)
…c changes as per Nat.
Quality Gate passedIssues Measures |
Issue #: https://citz-gdx.atlassian.net/browse/DESENG-692
Issue #: https://citz-gdx.atlassian.net/browse/DESENG-671
Description of changes:
User Guide update ticket (if applicable):
N/A